package top.codedance.iotp.converter.service.master;

import top.codedance.iotp.converter.ConverterApplication;
import top.codedance.iotp.converter.bean.MasterConnectParam;
import top.codedance.iotp.converter.bean.SlaveTask;
import top.codedance.iotp.converter.service.master.listenner.SerialPortListener;
import top.codedance.iotp.converter.service.slave.Slave;
import com.fazecast.jSerialComm.SerialPort;
import org.apache.log4j.Logger;

import java.util.*;

public class SerialMaster extends AdapterSerialMaster {
    private static Logger logger = ConverterApplication.getLogger(SerialMaster.class);
    private SerialPort mSerialPort;
    private static volatile LinkedList<SlaveTask> tasks = new LinkedList<>();

    public SerialMaster(MasterConnectParam mcp) {
        super(mcp);
        ConverterApplication.workerPool.submit(() -> {
            while (true){
                for (SlaveTask t : tasks){
                    if(System.currentTimeMillis() - t.getTimestamp() > 2000){
                        tasks.remove(t);
                        logger.debug("Remove task : " + t.getTimestamp());
                    }
                }
                try{
                    Thread.sleep(500);
                } catch (Exception e){}
            }
        });
    }

    @Override
    public void init() {
        try {
            connect();
        }catch (Exception ex){
            logger.info(ex.getMessage());
            System.exit(0);
        }
        long time = System.currentTimeMillis();
        while (true) {
            if (mSerialPort == null || !mSerialPort.isOpen()) {
                try {
                    connect();
                }catch (Exception ex){
                    logger.info(ex.getMessage());
                }
                logger.info("Master is reconnect!");
            }else{
                if(System.currentTimeMillis() - time > 1000 * 3600 * 2){
                    destory();
                    try {
                        connect();
                    }catch (Exception ex){
                        logger.info(ex.getMessage());
                    }
                    time = System.currentTimeMillis();
                }
            }
            try {
                Thread.sleep(3000);
            } catch (Exception ex) { }
        }
    }

    @Override
    public void connect() {
        mSerialPort = SerialPort.getCommPort(param.getComName());
        mSerialPort.setFlowControl(SerialPort.FLOW_CONTROL_DISABLED);
        mSerialPort.setComPortParameters(param.getBaudRate(), param.getDataBits(), param.getStopBits(), param.getParity());
        mSerialPort.addDataListener(new SerialPortListener(this));
        if (!mSerialPort.openPort(0, 512, 512)) {
            mSerialPort = null;
        }
        logger.info("Serial master is running!");
    }

    @Override
    public boolean isOpen() {
        return mSerialPort.isOpen();
    }

    @Override
    public void destory() {
        if(mSerialPort != null && mSerialPort.isOpen()) {
            mSerialPort.closePort();
        }
    }

    @Override
    public byte[] request(byte[] data) {
        return new byte[0];
    }

    @Override
    public synchronized void recover(byte[] data) {
        if(tasks.size() > 0){
            ConverterApplication.workerPool.submit(() -> {
                SlaveTask task = tasks.poll();
                Slave slave = task.getSlave();
                slave.send(data);
                logger.debug("Return data to slave[" + slave.getCom() + "]");
            });
        }else{
            Set<Map.Entry<Integer, Slave>> entries = ConverterApplication.slaves.entrySet();
            for (Map.Entry<Integer, Slave> entry : entries){
                ConverterApplication.workerPool.submit(() -> {
                    Slave slave = entry.getValue();
                    slave.send(data);
                    logger.debug("Return data to slave[" + slave.getCom() + "]");
                });
            }
        }
    }

    @Override
    public synchronized void send(Slave slave, byte[] data) {
        if(mSerialPort == null || !mSerialPort.isOpen()) {
            connect();
        }
        if(mSerialPort != null && mSerialPort.isOpen()) {
            int i = mSerialPort.writeBytes(data, data.length);
            if(i > 0){
                tasks.offer(new SlaveTask(slave, System.currentTimeMillis()));
                logger.debug("Task queue size: " + tasks.size());
            }
        }
    }
}
